package com.tianji.learning.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.ssh.JschUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.tianji.learning.domin.po.LearningLesson;
import com.tianji.learning.domin.po.LearningRecord;
import com.tianji.learning.mapper.LearningLessonMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.crypto.agreement.srp.SRP6Util;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.validation.constraints.NotNull;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.DelayQueue;

@Component
@Slf4j
@RequiredArgsConstructor
public class LearningRecordDelayTaskHadler implements ApplicationContextAware {

    private final StringRedisTemplate redisTemplate;
    private final String Key_TEMPLATE = "learning:record:{}";
    private DelayQueue<DelayTask<DelayTaskData>> delayQueue = new DelayQueue<>();
    private final LearningRecordMapper recordMapper;
    private final LearningLessonMapper lessonMapper;
    private static Boolean flay = true;
    @PostConstruct
    public void init() {
        new Thread(()->handleDelayTask()).start();
        CompletableFuture.runAsync(this::handleDelayTask);

    }

    @PreDestroy
    public void destroy() {
        flay = false;
    }



    /**
     * 处理延时检测任务
     */
    private void handleDelayTask() {
        log.info("开始执行播放进度延时检测任务扫描...");

        while (flay) {
            try {
                //1.从延时队列中尝试 take 获取延时检测任务
                DelayTask<DelayTaskData> delayTake = delayQueue.take();
                DelayTaskData date = delayTake.getDate();
                log.info("获取延时检测任务，任务数据：{}", date);
                //2.从redis中查询实时播放进度
                LearningRecord redisRecord = readLearningRecordFromCache(date.getLessonId(), date.getSectionId());
                //3.比较延时任务中的本地进度，与redis中的进度时间是否一致
                //3.1 如果不一致，证明用户还在观看，直接跳过
                if (ObjectUtil.isEmpty(redisRecord) || ObjectUtil.notEqual(redisRecord.getMoment(), date.getMoment())) {
                    continue;
                }
                //3.2 如果一致，则说明用户已经停止观看，需要将Redis中的实时进度 更新到学习记录表，并更新 课表数据
                //3.2.1 更新学习记录表（主要是更新 播放进度moment
                redisRecord.setFinished(null);
                recordMapper.updateById(redisRecord);
                //3.2.2 更新课表数据（更新 最近学习小节ID，最近学习时间）
                LearningLesson lesson = new LearningLesson();
                lesson.setId(date.getLessonId());
                lesson.setLatestSectionId(date.getSectionId());
                lesson.setLatestLearnTime(LocalDateTime.now().minusSeconds(20));
                lessonMapper.updateById(lesson);
            } catch (Exception e) {
                log.error("处理某个延时检测任务异常：{}", e);

            }

        }
    }

    /**
     * 类中方法使用前进行方法
     *
     * @param applicationContext the ApplicationContext object to be used by this object
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {

    }

    /**
     * 1.从缓存中读取学习记录
     *
     * @param lessonId  课表ID
     * @param sectionId 小节ID
     * @return
     */
    public LearningRecord readLearningRecordFromCache(@NotNull(message = "课表id不能为空") Long lessonId, @NotNull(message = "节的id不能为空") Long sectionId) {
        log.info("从缓存中查询学习记录，课表ID：{}，小节ID{}", lessonId, sectionId);
        //1.组装大key
        String key = StrUtil.format(Key_TEMPLATE, lessonId);

        //2.组装hashkey
        String hashKey = sectionId.toString();

        //3.发起查询，得到hashvalue
        String hashValue = (String) redisTemplate.opsForHash().get(key, hashKey);
        if (StrUtil.isBlank(hashValue)) {
            log.warn("缓存中没有查询到学习记录，课表ID：{}，小节ID{}", lessonId, sectionId);
            return null;
        }

        //4.将hashvalue转换成LearningRecord对象
        LearningRecord learningRecord = JSONUtil.toBean(hashValue, LearningRecord.class);
        return learningRecord;
    }

    /**
     * 2.将学习记录写入缓存
     *
     * @param oldRecord 数据库的学习记录
     */
    public void writeLearningRecordToCache(LearningRecord oldRecord) {
        log.info("将学习记录写入缓存，学习记录：{}", oldRecord);
        //1.组装大key
        String key = StrUtil.format(Key_TEMPLATE, oldRecord.getLessonId());

        //2.组装hashkey
        String hashKey = oldRecord.getSectionId().toString();

        //3.组装hashvalue
        RedisRecordData bean = BeanUtil.toBean(oldRecord, RedisRecordData.class);
        String hashValue = JSONUtil.toJsonStr(bean);
        redisTemplate.opsForHash().put(key, hashKey, hashValue);
        //设置过期时间
        redisTemplate.expire(key, Duration.ofMillis(1));


    }

    /**
     * 将学习记录暂存到redis，并且提交一个延时任务
     *
     * @param learningRecord
     */
    public void addLearningRecordAndDelayCheckTask(LearningRecord learningRecord) {
        log.info("将学习记录暂存到redis，学习记录：{}", learningRecord);
        //1.暂存延时检测任务到redis
        writeLearningRecordToCache(learningRecord);
        //2.提交延时检测任务
        DelayTaskData bean = BeanUtil.toBean(learningRecord, DelayTaskData.class);
        delayQueue.add(DelayTask.of(Duration.ofSeconds(20), bean));
    }

    /**
     * 删除redis学习记录缓存
     *
     * @param lessonId
     * @param sectionId
     */
    public void cleanLearningRecordCache(@NotNull(message = "课表id不能为空") Long lessonId, @NotNull(message = "节的id不能为空") Long sectionId) {
        log.info("删除redis学习记录缓存，课表ID：{}，小节ID{}", lessonId, sectionId);
        //1.组装大key
        String key = StrUtil.format(Key_TEMPLATE, lessonId);

        //2.组装hashkey
        String hashKey = sectionId.toString();

        //3.删除缓存
        redisTemplate.opsForHash().delete(key, hashKey);
    }


    @Data
    class RedisRecordData {
        /**
         * 学习记录的id
         */
        @TableId(value = "id", type = IdType.ASSIGN_ID)
        private Long id;

        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;

        /**
         * 是否完成学习，默认false
         */
        private Boolean finished;

    }

    @Data
    class DelayTaskData {
        /**
         * 对应课表的id
         */
        private Long lessonId;

        /**
         * 对应小节的id
         */
        private Long sectionId;


        /**
         * 视频的当前观看时间点，单位秒
         */
        private Integer moment;
    }

}
